//
// Created by Tony on 2024/6/23.
//

#include "Code.h"
#include "../ByteCodeReader.h"
#include "../instructions/InstructionFactory.h"
#include <cstdint>
#include <iomanip>
#include <sstream>

namespace RtCode {

int32_t Code::addOpCode(OpCode opcode) {
    if (opcode == OpCode::RETURN) {
        mHasReturn = true;
    }
    mCode.push_back(static_cast<uint8_t>(opcode));
    return static_cast<int32_t>(mCode.size() - 1);
}

void Code::addOperandUint16(uint16_t operand) {
    Union16 val{.u16 = operand};
    mCode.push_back(val.hu8);
    mCode.push_back(val.lu8);
}

void Code::updateOperandUint16(int32_t index, uint16_t operand) {
    Union16 val{.u16 = operand};
    mCode[index] = val.hu8;
    mCode[index + 1] = val.lu8;
}

int32_t Code::addOpCodeWithUint16(OpCode opcode, uint16_t operand) {
    auto ret = addOpCode(opcode);
    addOperandUint16(operand);
    return ret;
}

void Code::addOperandInt16(int16_t operand) {
    Union16 val{.i16 = operand};
    addOperandUint16(val.u16);
}

void Code::updateOperandInt16(int32_t index, int16_t operand) {
    Union16 val{.i16 = operand};
    updateOperandUint16(index, val.u16);
}

int32_t Code::addOpCodeWithInt16(OpCode opcode, int16_t operand) {
    auto ret = addOpCode(opcode);
    addOperandInt16(operand);
    return ret;
}

const vector<uint8_t>& Code::getCode() const { return mCode; }

bool Code::hasReturn() const { return mHasReturn; }

uint8_t Code::readUint8(uint32_t index) const { return mCode[index]; }

uint16_t Code::readUint16(uint32_t index) const {
    auto hu8 = mCode[index];
    auto lu8 = mCode[index + 1];
    Union16 v{.hu8 = hu8, .lu8 = lu8};
    return v.u16;
}

int16_t Code::readInt16(uint32_t index) const {
    auto u16 = readUint16(index);
    Union16 v{.u16 = u16};
    return v.i16;
}

void Code::setLocalSlotCount(uint16_t maxLocals) {
    mMaxLocals = maxLocals;
    if (mMaxStack < maxLocals) {
        mMaxStack = maxLocals;
        mTop = maxLocals;
    }
}

uint16_t Code::getLocalSlotCount() const { return mMaxLocals; }

uint16_t Code::getMaxStack() const { return mMaxStack; }

uint16_t Code::incTop(uint16_t i) {
    mTop += i;
    if (mTop > mMaxStack) {
        mMaxStack = mTop;
    }
    return mTop;
}

uint16_t Code::decTop(uint16_t i) {
    mTop -= i;
    return mTop;
}

string Code::toString() {
    using std::endl;
    using std::setw;

    Runtime::ByteCodeReader reader(shared_from_this(), 0);
    std::stringstream ss;

    while (true) {
        auto pc = reader.getNextPC();
        if (reader.getNextPC() >= size()) {
            break;
        }

        auto opcode = reader.readOpCode();
        auto* instr = Runtime::InstructionFactory::create(opcode);
        instr->fetchOperands(reader);
        ss << "pc: " << std::left << setw(4) << pc << "  " << instr->toString() << endl;
    }

    return ss.str();
}

} // namespace RtCode